﻿using System;
using Microsoft.Xna.Framework;

namespace FairyGUI.Test.Scenes
{
	public class JoystickModule : EventDispatcher
	{
		float _InitX;
		float _InitY;
		float _startStageX;
		float _startStageY;
		float _lastStageX;
		float _lastStageY;
		GButton _button;
		GObject _touchArea;
		GObject _thumb;
		GObject _center;
		int touchId;
		GTweener _tweener;

		public EventListener onMove { get; private set; }
		public EventListener onEnd { get; private set; }

		public int radius { get; set; }

		public JoystickModule(GComponent mainView)
		{
			onMove = new EventListener(this, "onMove");
			onEnd = new EventListener(this, "onEnd");

			_button = mainView.GetChild("joystick").asButton;
			_button.changeStateOnClick = false;
			_thumb = _button.GetChild("thumb");
			_touchArea = mainView.GetChild("joystick_touch");
			_center = mainView.GetChild("joystick_center");

			_InitX = _center.x + _center.width / 2;
			_InitY = _center.y + _center.height / 2;
			touchId = -1;
			radius = 150;

			_touchArea.onTouchBegin.Add(this.OnTouchBegin);
			_touchArea.onTouchMove.Add(this.OnTouchMove);
			_touchArea.onTouchEnd.Add(this.OnTouchEnd);
		}

		public void Trigger(EventContext context)
		{
			OnTouchBegin(context);
		}

		private void OnTouchBegin(EventContext context)
		{
			if (touchId == -1) //First touch
			{
				InputEvent evt = (InputEvent)context.data;
				touchId = evt.touchId;

				if (_tweener != null)
				{
					_tweener.Kill();
					_tweener = null;
				}

				Vector2 pt = GRoot.inst.GlobalToLocal(new Vector2(evt.x, evt.y));
				float bx = pt.X;
				float by = pt.Y;
				_button.selected = true;

				if (bx < 0)
					bx = 0;
				else if (bx > _touchArea.width)
					bx = _touchArea.width;

				if (by > GRoot.inst.height)
					by = GRoot.inst.height;
				else if (by < _touchArea.y)
					by = _touchArea.y;

				_lastStageX = bx;
				_lastStageY = by;
				_startStageX = bx;
				_startStageY = by;

				_center.visible = true;
				_center.SetPosition(bx - _center.width / 2, by - _center.height / 2);
				_button.SetPosition(bx - _button.width / 2, by - _button.height / 2);

				float deltaX = bx - _InitX;
				float deltaY = by - _InitY;
				float degrees = (float)Math.Atan2(deltaY, deltaX) * 180 / (float)Math.PI;
				_thumb.rotation = degrees + 90;

				context.CaptureTouch();
			}
		}

		private void OnTouchEnd(EventContext context)
		{
			InputEvent inputEvt = (InputEvent)context.data;
			if (touchId != -1 && inputEvt.touchId == touchId)
			{
				touchId = -1;
				_thumb.rotation = _thumb.rotation + 180;
				_center.visible = false;
				_tweener = _button.TweenMove(new Vector2(_InitX - _button.width / 2, _InitY - _button.height / 2), 0.3f)
					.OnComplete(() =>
						{
							_tweener = null;
							_button.selected = false;
							_thumb.rotation = 0;
							_center.visible = true;
							_center.SetPosition(_InitX - _center.width / 2, _InitY - _center.height / 2);
						}
					);

				this.onEnd.Call();
			}
		}

		private void OnTouchMove(EventContext context)
		{
			InputEvent evt = (InputEvent)context.data;
			if (touchId != -1 && evt.touchId == touchId)
			{
				Vector2 pt = GRoot.inst.GlobalToLocal(new Vector2(evt.x, evt.y));
				float bx = pt.X;
				float by = pt.Y;
				float moveX = bx - _lastStageX;
				float moveY = by - _lastStageY;
				_lastStageX = bx;
				_lastStageY = by;
				float buttonX = _button.x + moveX;
				float buttonY = _button.y + moveY;

				float offsetX = buttonX + _button.width / 2 - _startStageX;
				float offsetY = buttonY + _button.height / 2 - _startStageY;

				float rad = (float)Math.Atan2(offsetY, offsetX);
				float degree = rad * 180 / (float)Math.PI;
				_thumb.rotation = degree + 90;

				float maxX = radius * (float)Math.Cos(rad);
				float maxY = radius * (float)Math.Sin(rad);
				if ((float)Math.Abs(offsetX) > (float)Math.Abs(maxX))
					offsetX = maxX;
				if ((float)Math.Abs(offsetY) > (float)Math.Abs(maxY))
					offsetY = maxY;

				buttonX = _startStageX + offsetX;
				buttonY = _startStageY + offsetY;
				if (buttonX < 0)
					buttonX = 0;
				if (buttonY > GRoot.inst.height)
					buttonY = GRoot.inst.height;

				_button.SetPosition(buttonX - _button.width / 2, buttonY - _button.height / 2);

				this.onMove.Call(degree);
			}
		}
	}
}